
module Authorizer
	class Unauthorized < Exception
	end
	class Permissions
		
		def initialize
			@__perms ||= Hash.new 
		end

		def allow(usr, obj, *ops)
			#Since YAML does not support Hashes with default
			#constructor we have to take this cumbersome approach
		
			@__perms[usr] ||= Hash.new
			@__perms[usr][obj] ||= Array.new
			@__perms[usr][obj] += ops.flatten
			@__perms[usr][obj].uniq!
		end
		
		def allow_read(usr,obj)
			allow(usr,obj,[:read])
		end

		def allow_write(usr,obj)
			allow(usr,obj,[:write])
		end

		def deny(usr, obj, *ops)
			return if (@__perms[usr] == nil) || (@__perms[usr][obj] == nil)
			begin
				ops.each {|op|@__perms[usr][obj].delete(op)}
			end
			@__perms[usr].delete(obj) unless @__perms[usr][obj].size > 0
			@__perms.delete(usr) unless @__perms[usr].size > 0
		end
		
		def deny_read(usr,obj)
			deny(usr,obj,[:read])
		end

		def deny_write(usr,obj)
			deny(usr,obj,[:read])
		end

		def authorized?(op, args)
			usr,obj=args[0..1]
			begin
				raise unless op == ( @__perms[usr.to_s][obj.to_s] & op )
			rescue
				raise "User #{usr} unauthorized to #{op} object #{obj} because #{$!}" 
			end
			return yield(args) 
		end

		def each
			@__perms.each_pair{ |usr, val|
				val.each_pair{|obj,ops|
				            yield usr, obj, ops 
				}
			}
		end
	end
	
end


class Module

	def check_access(method, op )
	 real_method_name = :"__real_#{method}"
	 alias_method real_method_name , method
	 define_method(method) {  |*args|		 
			ret=nil	
			begin
				self.permissions.authorized?(op, args) do
					ret = self.send(real_method_name, *args)
				end
			rescue Exception
				puts "ERROR: ====================== #{$!}"
				raise $!  unless self.class.method_defined?(:unauthorized_handler)
				ret = self.send(:unauthorized_handler, $!)			
			end
			return ret
	}
	 
   end
	
   def check_read(method)
	   check_access(method, [:read])
   end
   
   def check_write(method)
	   check_access(method, [:write])
   end

end

if __FILE__ == $0 then
	
	

end  
